{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "SzKwuqYESWwm"
   },
   "source": [
    "##### Copyright 2020 The Cirq Developers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "id": "4yPUsdJxSXFq"
   },
   "outputs": [],
   "source": [
    "# @title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "# https://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "dVkNQc0WSIwk"
   },
   "source": [
    "# Quantum variational algorithm"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zC1qlUJoSXhm"
   },
   "source": [
    "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://quantumai.google/cirq/experiments/variational_algorithm\"><img src=\"https://quantumai.google/site-assets/images/buttons/quantumai_logo_1x.png\" />View on QuantumAI</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/quantumlib/Cirq/blob/main/docs/experiments/variational_algorithm.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/colab_logo_1x.png\" />Run in Google Colab</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://github.com/quantumlib/Cirq/blob/main/docs/experiments/variational_algorithm.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/github_logo_1x.png\" />View source on GitHub</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a href=\"https://storage.googleapis.com/tensorflow_docs/Cirq/docs/experiments/variational_algorithm.ipynb\"><img src=\"https://quantumai.google/site-assets/images/buttons/download_icon_1x.png\" />Download notebook</a>\n",
    "  </td>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "y0TUn0KcBcSO"
   },
   "source": [
    "In this tutorial, we use the variational quantum eigensolver [[1]](https://arxiv.org/abs/1304.3061) (VQE) in Cirq to optimize a simple Ising model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "bd9529db1c0b"
   },
   "outputs": [],
   "source": [
    "try:\n",
    "    import cirq\n",
    "except ImportError:\n",
    "    print(\"installing cirq...\")\n",
    "    !pip install --quiet cirq\n",
    "    print(\"installed cirq.\")\n",
    "    import cirq"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "xcn4Ncad_5pT"
   },
   "source": [
    "## Background: Variational Quantum Algorithm\n",
    "\n",
    "The [variational method](https://en.wikipedia.org/wiki/Variational_method_(quantum_mechanics)) in quantum theory is a classical method for finding low energy states of a quantum system. The rough idea of this method is that one defines a trial wave function (sometimes called an *ansatz*) as a function of some parameters, and then one finds the values of these parameters that minimize the expectation value of the energy with respect to these parameters. This minimized ansatz is then an approximation to the lowest energy eigenstate, and the expectation value serves as an upper bound on the energy of the ground state.\n",
    "\n",
    "In the last few years (see [[1]](https://arxiv.org/abs/1304.3061) and [[2]](https://arxiv.org/abs/1507.08969), for example), it has been realized that quantum computers can mimic the classical technique and that a quantum computer does so with certain advantages. In particular, when one applies the classical variational method to a system of $n$ qubits, an exponential number (in $n$) of complex numbers is necessary to generically represent the wave function of the system. However, with a quantum computer, one can directly produce this state using a parameterized quantum circuit with less than exponential parameters, and then use repeated measurements to estimate the expectation value of the energy.\n",
    "\n",
    "This idea has led to a class of algorithms known as variational quantum algorithms. Indeed this approach is not just limited to finding low energy eigenstates, but minimizing any objective function that can be expressed as a quantum observable. It is an open question to identify under what conditions these quantum variational algorithms will succeed, and exploring this class of algorithms is a key part of the research for noisy intermediate-scale quantum computers [[3]](https://arxiv.org/abs/1801.00862).\n",
    "\n",
    "The classical problem we will focus on is the 2D +/- Ising model with transverse field ([ISING](http://iopscience.iop.org/article/10.1088/0305-4470/15/10/028/meta)). This problem is NP-complete. It is highly unlikely that quantum computers will be able to efficiently solve it across all instances because it is generally believed that quantum computers cannot solve all NP-complete problems in polynomial time. Yet this type of problem is illustrative of the general class of problems that Cirq is designed to tackle.\n",
    "\n",
    "\n",
    "Let's define the problem. Consider the energy function\n",
    "\n",
    "$E(s_1,\\dots,s_n) = \\sum_{\\langle i,j \\rangle} J_{i,j}s_i s_j + \\sum_i h_i s_i$\n",
    "\n",
    "where here each $s_i, J_{i,j}$, and $h_i$ are either +1 or -1. Here each index i is associated with a bit on a square lattice, and the $\\langle i,j \\rangle$ notation means sums over neighboring bits on this lattice. The problem we would like to solve is, given $J_{i,j}$, and $h_i$, find an assignment of $s_i$ values that minimize $E$.\n",
    "\n",
    "How does a variational quantum algorithm work for this? One approach is to consider $n$ qubits and associate them with each of the bits in the classical problem. This maps the classical problem onto the quantum problem of minimizing the expectation value of the observable\n",
    "\n",
    "$H=\\sum_{\\langle i,j \\rangle} J_{i,j} Z_i Z_j + \\sum_i h_iZ_i$\n",
    "\n",
    "Then one defines a set of parameterized quantum circuits, i.e., a quantum circuit where the gates (or more general quantum operations) are parameterized by some values. This produces an ansatz state\n",
    "\n",
    "$|\\psi(p_1, p_2, \\dots, p_k)\\rangle$\n",
    "\n",
    "where $p_i$ are the parameters that produce this state (here we assume a pure state, but mixed states are of course possible).\n",
    "\n",
    "The variational algorithm then works by noting that one can obtain the value of the objective function for a given ansatz state by\n",
    "\n",
    "1. Prepare the ansatz state.\n",
    "2. Make a measurement which samples from some terms in H.\n",
    "3. Goto 1.\n",
    "\n",
    "Note that one cannot always measure $H$ directly (without the use of quantum phase estimation). So one often relies on the linearity of expectation values to measure parts of $H$ in step 2. One always needs to repeat the measurements to obtain an estimate of the expectation value. How many measurements needed to achieve a given accuracy is beyond the scope of this tutorial, but Cirq can help investigate this question.\n",
    "\n",
    "The above shows that one can use a quantum computer to obtain estimates of the objective function for the ansatz. This can then be used in an outer loop to try to obtain parameters for the lowest value of the objective function. For these best parameter, one can then use that best ansatz to produce samples of solutions to the problem, which obtain a hopefully good approximation for the lowest possible value of the objective function.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "cfsYdZw6_5pU"
   },
   "source": [
    "## Create a circuit on a Grid\n",
    "\n",
    "To build the above variational quantum algorithm using Cirq, one begins by building the appropriate circuit.  Because the problem we have defined has a natural structure on a grid, we will use Cirq’s built-in `cirq.GridQubit`s as our qubits. We will demonstrate some of how this works in an interactive Python environment, the following code can be run in series in a Python environment where you have Cirq installed.  For more about circuits and how to create them, see the [Tutorial](../start/basics.ipynb) or the [Circuits](../build/circuits.ipynb) page."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "5TV_rMxX_5pW"
   },
   "outputs": [],
   "source": [
    "# define the length and width of the grid.\n",
    "length = 3\n",
    "\n",
    "# define qubits on the grid.\n",
    "qubits = cirq.GridQubit.square(length)\n",
    "\n",
    "print(qubits)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "D3obTsFs_5pa"
   },
   "source": [
    "Here we see that we've created a bunch of `cirq.GridQubits`, which have a row and column, indicating their position on a grid.\n",
    "\n",
    "Now that we have some qubits, let us construct a `cirq.Circuit` on these qubits. For example, suppose we want to apply the Hadamard gate `cirq.H` to every qubit whose row index plus column index is even, and an `cirq.X` gate to every qubit whose row index plus column index is odd. To do this, we write:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "dy3VFNMx_5pa"
   },
   "outputs": [],
   "source": [
    "circuit = cirq.Circuit()\n",
    "circuit.append(cirq.H(q) for q in qubits if (q.row + q.col) % 2 == 0)\n",
    "circuit.append(cirq.X(q) for q in qubits if (q.row + q.col) % 2 == 1)\n",
    "\n",
    "print(circuit)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "_iFQ7Zwu_5pi"
   },
   "source": [
    "## Creating the Ansatz\n",
    "\n",
    "One convenient pattern is to use a python [Generator](https://wiki.python.org/moin/Generators) for defining sub-circuits or layers in our algorithm.  We will define a function that takes in the relevant parameters and then yields the operations for the sub-circuit, and then this can be appended to the `cirq.Circuit`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "rLayzy4P_5pj"
   },
   "outputs": [],
   "source": [
    "def rot_x_layer(length, half_turns):\n",
    "    \"\"\"Yields X rotations by half_turns on a square grid of given length.\"\"\"\n",
    "\n",
    "    # Define the gate once and then re-use it for each Operation.\n",
    "    rot = cirq.XPowGate(exponent=half_turns)\n",
    "\n",
    "    # Create an X rotation Operation for each qubit in the grid.\n",
    "    for i in range(length):\n",
    "        for j in range(length):\n",
    "            yield rot(cirq.GridQubit(i, j))\n",
    "\n",
    "\n",
    "# Create the circuit using the rot_x_layer generator\n",
    "circuit = cirq.Circuit()\n",
    "circuit.append(rot_x_layer(2, 0.1))\n",
    "print(circuit)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "DrO5W2ie_5pl"
   },
   "source": [
    "Another important concept here is that the rotation gate is specified in *half turns* ($ht$). For a rotation about `X`, the gate is:\n",
    "\n",
    "$\\cos(ht * \\pi) I + i \\sin(ht * \\pi) X$\n",
    "\n",
    "There is a lot of freedom defining a variational ansatz. Here we will do a variation on a QAOA strategy [[4]](https://arxiv.org/abs/1411.4028) and define an ansatz related to the problem we are trying to solve.\n",
    "\n",
    "First, we need to choose how the instances of the problem are represented. These are the values $J$ and $h$ in the Hamiltonian definition. We represent them as two-dimensional arrays (lists of lists). For $J$ we use two such lists, one for the row links and one for the column links.\n",
    "\n",
    "Here is a snippet that we can use to generate random problem instances:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "6E5BjSxF_5pm"
   },
   "outputs": [],
   "source": [
    "import random\n",
    "\n",
    "\n",
    "def rand2d(rows, cols):\n",
    "    return [[random.choice([+1, -1]) for _ in range(cols)] for _ in range(rows)]\n",
    "\n",
    "\n",
    "def random_instance(length):\n",
    "    # transverse field terms\n",
    "    h = rand2d(length, length)\n",
    "    # links within a row\n",
    "    jr = rand2d(length - 1, length)\n",
    "    # links within a column\n",
    "    jc = rand2d(length, length - 1)\n",
    "    return (h, jr, jc)\n",
    "\n",
    "\n",
    "h, jr, jc = random_instance(3)\n",
    "print(f'transverse fields: {h}')\n",
    "print(f'row j fields: {jr}')\n",
    "print(f'column j fields: {jc}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zsq_177Q_5po"
   },
   "source": [
    "In the code above, the actual values will be different for each individual run because they are using `random.choice`.\n",
    "\n",
    "Given this definition of the problem instance, we can now introduce our ansatz. It will consist of one step of a circuit made up of:\n",
    "\n",
    "0\\. Apply an initial mixing step that puts all the qubits into the $|+\\rangle=\\frac{1}{\\sqrt{2}}(|0\\rangle+|1\\rangle)$ state.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "50ccff9234b2"
   },
   "outputs": [],
   "source": [
    "def prepare_plus_layer(length):\n",
    "    for i in range(length):\n",
    "        for j in range(length):\n",
    "            yield cirq.H(cirq.GridQubit(i, j))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "49482d550033"
   },
   "source": [
    "1\\. Apply a `cirq.ZPowGate` for the same parameter for all qubits where the transverse field term $h$ is $+1$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "XtYIZSef_5po"
   },
   "outputs": [],
   "source": [
    "def rot_z_layer(h, half_turns):\n",
    "    \"\"\"Yields Z rotations by half_turns conditioned on the field h.\"\"\"\n",
    "    gate = cirq.ZPowGate(exponent=half_turns)\n",
    "    for i, h_row in enumerate(h):\n",
    "        for j, h_ij in enumerate(h_row):\n",
    "            if h_ij == 1:\n",
    "                yield gate(cirq.GridQubit(i, j))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "iSizAkjE_5pq"
   },
   "source": [
    "2\\. Apply a `cirq.CZPowGate` for the same parameter between all qubits where the coupling field term $J$ is $+1$. If the field is $-1$, apply `cirq.CZPowGate` conjugated by $X$ gates on all qubits."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "jo9pqBlJ_5pq"
   },
   "outputs": [],
   "source": [
    "def rot_11_layer(jr, jc, half_turns):\n",
    "    \"\"\"Yields rotations about |11> conditioned on the jr and jc fields.\"\"\"\n",
    "    cz_gate = cirq.CZPowGate(exponent=half_turns)\n",
    "    for i, jr_row in enumerate(jr):\n",
    "        for j, jr_ij in enumerate(jr_row):\n",
    "            q = cirq.GridQubit(i, j)\n",
    "            q_1 = cirq.GridQubit(i + 1, j)\n",
    "            if jr_ij == -1:\n",
    "                yield cirq.X(q)\n",
    "                yield cirq.X(q_1)\n",
    "            yield cz_gate(q, q_1)\n",
    "            if jr_ij == -1:\n",
    "                yield cirq.X(q)\n",
    "                yield cirq.X(q_1)\n",
    "\n",
    "    for i, jc_row in enumerate(jc):\n",
    "        for j, jc_ij in enumerate(jc_row):\n",
    "            q = cirq.GridQubit(i, j)\n",
    "            q_1 = cirq.GridQubit(i, j + 1)\n",
    "            if jc_ij == -1:\n",
    "                yield cirq.X(q)\n",
    "                yield cirq.X(q_1)\n",
    "            yield cz_gate(q, q_1)\n",
    "            if jc_ij == -1:\n",
    "                yield cirq.X(q)\n",
    "                yield cirq.X(q_1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "DI7wQure_5ps"
   },
   "source": [
    "3\\. Apply an `cirq.XPowGate` for the same parameter for all qubits. This is the method `rot_x_layer` we have written above.\n",
    "\n",
    "Putting all together, we can create a step that uses just three parameters. Below is the code, which uses the generator for each of the layers (note to advanced Python users: this code does not contain a bug in using `yield` due to the auto flattening of the `OP_TREE concept`. Typically, one would want to use `yield` from here, but this is not necessary):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "M6Z2hxsG_5pt"
   },
   "outputs": [],
   "source": [
    "def initial_step(length):\n",
    "    yield prepare_plus_layer(length)\n",
    "\n",
    "\n",
    "def one_step(h, jr, jc, x_half_turns, h_half_turns, j_half_turns):\n",
    "    length = len(h)\n",
    "    yield rot_z_layer(h, h_half_turns)\n",
    "    yield rot_11_layer(jr, jc, j_half_turns)\n",
    "    yield rot_x_layer(length, x_half_turns)\n",
    "\n",
    "\n",
    "h, jr, jc = random_instance(3)\n",
    "\n",
    "circuit = cirq.Circuit()\n",
    "circuit.append(initial_step(len(h)))\n",
    "circuit.append(one_step(h, jr, jc, 0.1, 0.2, 0.3))\n",
    "print(circuit)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "y5E_9AYw_5pv"
   },
   "source": [
    "Here we see that we have chosen particular parameter values $(0.1, 0.2, 0.3)$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zAwTXwc7_5pv"
   },
   "source": [
    "## Simulation\n",
    "\n",
    "In Cirq, the simulators make a distinction between a *run* and a *simulation*. A *run* only allows for a simulation that mimics the actual quantum hardware. For example, it does not allow for access to the amplitudes of the wave function of the system, since that is not experimentally accessible. *Simulate* commands, however, are broader and allow different forms of simulation. When prototyping small circuits, it is useful to execute *simulate* methods, but one should be wary of relying on them when running against actual hardware."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "PXpn3xvT_5pv"
   },
   "outputs": [],
   "source": [
    "simulator = cirq.Simulator()\n",
    "circuit = cirq.Circuit()\n",
    "circuit.append(initial_step(len(h)))\n",
    "circuit.append(one_step(h, jr, jc, 0.1, 0.2, 0.3))\n",
    "circuit.append(cirq.measure(*qubits, key='x'))\n",
    "results = simulator.run(circuit, repetitions=100)\n",
    "print(results.histogram(key='x'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "DEIjXRgt_5px"
   },
   "source": [
    "Note that we have run the simulation 100 times and produced a histogram of the counts of the measurement results. What are the keys in the histogram counter? Note that we have passed in the order of the qubits. This ordering is then used to translate the order of the measurement results to a register using a [big endian representation](https://en.wikipedia.org/wiki/Endianness).\n",
    "\n",
    "For our optimization problem, we want to calculate the value of the objective function for a given result run. One way to do this is using the raw measurement data from the result of `simulator.run`. Another way to do this is to provide to the histogram a method to calculate the objective: this will then be used as the key for the returned `Counter`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "Loy-K3YY_5py"
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "\n",
    "def energy_func(length, h, jr, jc):\n",
    "    def energy(measurements):\n",
    "        # Reshape measurement into array that matches grid shape.\n",
    "        meas_list_of_lists = [measurements[i * length : (i + 1) * length] for i in range(length)]\n",
    "        # Convert true/false to +1/-1.\n",
    "        pm_meas = 1 - 2 * np.array(meas_list_of_lists).astype(np.int32)\n",
    "\n",
    "        tot_energy = np.sum(pm_meas * h)\n",
    "        for i, jr_row in enumerate(jr):\n",
    "            for j, jr_ij in enumerate(jr_row):\n",
    "                tot_energy += jr_ij * pm_meas[i, j] * pm_meas[i + 1, j]\n",
    "        for i, jc_row in enumerate(jc):\n",
    "            for j, jc_ij in enumerate(jc_row):\n",
    "                tot_energy += jc_ij * pm_meas[i, j] * pm_meas[i, j + 1]\n",
    "        return tot_energy\n",
    "\n",
    "    return energy\n",
    "\n",
    "\n",
    "print(results.histogram(key='x', fold_func=energy_func(3, h, jr, jc)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "X_kzMzz1_5pz"
   },
   "source": [
    "One can then calculate the expectation value over all repetitions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "GH8_ww5a_5p0"
   },
   "outputs": [],
   "source": [
    "def obj_func(result):\n",
    "    energy_hist = result.histogram(key='x', fold_func=energy_func(3, h, jr, jc))\n",
    "    return np.sum([k * v for k, v in energy_hist.items()]) / result.repetitions\n",
    "\n",
    "\n",
    "print(f'Value of the objective function {obj_func(results)}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "9vMxRCBD_5p2"
   },
   "source": [
    "### Parameterizing the Ansatz\n",
    "\n",
    "Now that we have constructed a variational ansatz and shown how to simulate it using Cirq, we can think about optimizing the value. \n",
    "\n",
    "On quantum hardware, one would most likely want to have the optimization code as close to the hardware as possible. As the classical hardware that is allowed to inter-operate with the quantum hardware becomes better specified, this language will be better defined. Without this specification, however, Cirq also provides a useful concept for optimizing the looping in many optimization algorithms. This is the fact that many of the value in the gate sets can, instead of being specified by a float, be specified by a `sympy.Symbol`, and this `sympy.Symbol` can be substituted for a value specified at execution time.\n",
    "\n",
    "Luckily for us, we have written our code so that using parameterized values is as simple as passing `sympy.Symbol` objects where we previously passed float values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "D49TnPrt_5p2"
   },
   "outputs": [],
   "source": [
    "import sympy\n",
    "\n",
    "circuit = cirq.Circuit()\n",
    "alpha = sympy.Symbol('alpha')\n",
    "beta = sympy.Symbol('beta')\n",
    "gamma = sympy.Symbol('gamma')\n",
    "circuit.append(initial_step(len(h)))\n",
    "circuit.append(one_step(h, jr, jc, alpha, beta, gamma))\n",
    "circuit.append(cirq.measure(*qubits, key='x'))\n",
    "print(circuit)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "StwKTU7R_5p5"
   },
   "source": [
    "Note now that the circuit's gates are parameterized.\n",
    "\n",
    "Parameters are specified at runtime using a `cirq.ParamResolver`, which is just a dictionary from `Symbol` keys to runtime values. \n",
    "\n",
    "For instance:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "XOmpCqRq_5p5"
   },
   "outputs": [],
   "source": [
    "resolver = cirq.ParamResolver({'alpha': 0.1, 'beta': 0.3, 'gamma': 0.7})\n",
    "resolved_circuit = cirq.resolve_parameters(circuit, resolver)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "DEKrxQrL_5p7"
   },
   "source": [
    "resolves the parameters to actual values in the circuit.\n",
    "\n",
    "Cirq also has the concept of a *sweep*. A sweep is a collection of parameter resolvers. This runtime information is very useful when one wants to run many circuits for many different parameter values. Sweeps can be created to specify values directly (this is one way to get classical information into a circuit), or a variety of helper methods. For example suppose we want to evaluate our circuit over an equally spaced grid of parameter values. We can easily create this using `cirq.LinSpace`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "z43HpXbX_5p7"
   },
   "outputs": [],
   "source": [
    "sweep = (\n",
    "    cirq.Linspace(key='alpha', start=0.1, stop=0.9, length=5)\n",
    "    * cirq.Linspace(key='beta', start=0.1, stop=0.9, length=5)\n",
    "    * cirq.Linspace(key='gamma', start=0.1, stop=0.9, length=5)\n",
    ")\n",
    "results = simulator.run_sweep(circuit, params=sweep, repetitions=100)\n",
    "for result in results:\n",
    "    print(result.params.param_dict, obj_func(result))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "10JkH8Ka_5p9"
   },
   "source": [
    "### Finding the Minimum\n",
    "\n",
    "Now we have all the code, we do a simple grid search over values to find a minimal value. Grid search is not the best optimization algorithm, but is here simply illustrative."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "oFtTLBDq_5p-"
   },
   "outputs": [],
   "source": [
    "sweep_size = 10\n",
    "sweep = (\n",
    "    cirq.Linspace(key='alpha', start=0.0, stop=1.0, length=sweep_size)\n",
    "    * cirq.Linspace(key='beta', start=0.0, stop=1.0, length=sweep_size)\n",
    "    * cirq.Linspace(key='gamma', start=0.0, stop=1.0, length=sweep_size)\n",
    ")\n",
    "results = simulator.run_sweep(circuit, params=sweep, repetitions=100)\n",
    "\n",
    "min = None\n",
    "min_params = None\n",
    "for result in results:\n",
    "    value = obj_func(result)\n",
    "    if min is None or value < min:\n",
    "        min = value\n",
    "        min_params = result.params\n",
    "print(f'Minimum objective value is {min}.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Rjg59AG5_5p_"
   },
   "source": [
    "We've created a simple variational quantum algorithm using Cirq. Where to go next? Perhaps you can play around with the above code and work on analyzing the algorithms performance. Add new parameterized circuits and build an end to end program for analyzing these circuits."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "16726863ba02"
   },
   "source": [
    "## References\n",
    "\n",
    "[[1]](https://arxiv.org/abs/1304.3061) Alberto Peruzzo et al., \"A variational eigenvalue solver on a photonic quantum processor\", Nature Communications 5, no. 1 (2014): 4213.\n",
    "\n",
    "[[2]](https://arxiv.org/abs/1507.08969) Dave Wecker et al., \"Progress towards practical quantum variational algorithms\" Physical Review A 92, no. 4 (2015): 042303.\n",
    "\n",
    "[[3]](https://arxiv.org/abs/1801.00862) John Preskill., \"Quantum Computing in the NISQ era and beyond\", Quantum 2, (2018): 79.\n",
    "\n",
    "[[4]](https://arxiv.org/abs/1411.4028) Edward Farhi et al., \"A Quantum Approximate Optimization Algorithm\", arXiv:1411.4028 (2014)."
   ]
  }
 ],
 "metadata": {
  "colab": {
   "name": "variational_algorithm.ipynb",
   "toc_visible": true
  },
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
